package raft

import (
	"io"
	"os"
	"path/filepath"
	"xin/raft/lifecycle"
	"xin/raft/pb"
	"xin/raft/utils"

	"go.uber.org/zap"
	"google.golang.org/protobuf/proto"
)

// MetaStorage 存储raft元数据的接口
type MetaStorage interface {
	lifecycle.LifeCycle

	SetTerm(term uint64) error

	GetTerm() uint64

	SetVotedFor(votedFor uint64) error

	GetVoteFor() uint64

	SetTermAndVotedFor(term uint64, votedFor uint64) error
}

type LogStorage interface {
}

const raftMetaFileName = "raft.meta" // 元数据文件名称

type LocalRaftMetaStorage struct {
	pb.RaftMeta

	loaded   bool        // 是否已经加载
	path     string      // 持久化路径
	file     os.File     // 持久化文件
	fileinfo os.FileInfo // 持久化文件信息

	logger *zap.Logger
}

func NewLocalRaftMetaStorage(path string, logger *zap.Logger) *LocalRaftMetaStorage {
	return &LocalRaftMetaStorage{
		path:   path,
		logger: logger,
	}
}

func (l *LocalRaftMetaStorage) SetTerm(term uint64) error {
	l.Term = term
	return l.save()
}
func (l *LocalRaftMetaStorage) GetTerm() uint64 {
	return l.Term
}
func (l *LocalRaftMetaStorage) SetVotedFor(votedFor uint64) error {
	l.VoteFor = votedFor
	return l.save()
}
func (l *LocalRaftMetaStorage) GetVoteFor() uint64 {
	return l.VoteFor
}
func (l *LocalRaftMetaStorage) SetTermAndVotedFor(term uint64, votedFor uint64) error {
	l.Term = term
	l.VoteFor = votedFor
	return l.save()
}

func (l *LocalRaftMetaStorage) Init() error {
	err := utils.MakeDirNotExist(l.path)
	if err != nil {
		return err
	}

	filename := filepath.Join(l.path, raftMetaFileName)

	// 可读可写；不存在则创建
	file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, os.ModePerm)
	if err != nil {
		return err
	}

	l.file = *file

	fileinfo, err := os.Stat(filename)
	if err != nil {
		return nil
	}

	l.fileinfo = fileinfo

	err = l.load()
	if err != nil {
		return err
	}

	return nil
}

func (l *LocalRaftMetaStorage) Shutdown() error {
	err := l.save()
	if err != nil {
		l.logger.Error("failed to save raft meta", zap.Error(err))
	}
	return l.file.Close()
}

func (l *LocalRaftMetaStorage) load() error {
	if l.loaded {
		return nil
	}

	size := l.fileinfo.Size()
	if size == 0 {
		// 首次初始化文件
		l.Term = 0
		l.VoteFor = 0
		l.loaded = true
		return nil
	}

	data := make([]byte, size)

	_, err := l.file.Read(data)
	if err != nil {
		if err != io.EOF {
			return err
		}
	}

	err = proto.Unmarshal(data, l)
	if err != nil {
		return err
	}

	l.loaded = true
	return nil
}

func (l *LocalRaftMetaStorage) save() error {
	bytes, err := proto.Marshal(l)
	if err != nil {
		return err
	}

	n, err := l.file.WriteAt(bytes, 0) // 每次写入都是从 0 开始写。都是覆盖写。

	if err != nil {
		return err
	}

	l.logger.Info("save raft meta", zap.Int("len", n))

	return nil
}

type MemoryRaftLogStorage struct {
	term     uint64
	votedFor uint64
}

func NewMemoryRaftLogStorage() *MemoryRaftLogStorage {
	return &MemoryRaftLogStorage{}
}
func (m *MemoryRaftLogStorage) Init() error     { return nil }
func (m *MemoryRaftLogStorage) Shutdown() error { return nil }
func (m *MemoryRaftLogStorage) SetTerm(term uint64) error {
	m.term = term
	return nil
}
func (m *MemoryRaftLogStorage) GetTerm() uint64 {
	return m.term
}
func (m *MemoryRaftLogStorage) SetVotedFor(votedFor uint64) error {
	m.votedFor = votedFor
	return nil
}
func (m *MemoryRaftLogStorage) GetVoteFor() uint64 {
	return m.votedFor
}
func (m *MemoryRaftLogStorage) SetTermAndVotedFor(term uint64, votedFor uint64) error {
	m.term = term
	m.votedFor = votedFor
	return nil
}
